package cn.iocoder.yudao.module.system.service.file;

import cn.iocoder.yudao.module.system.util.FFmpegUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 文件处理服务类
 * 负责处理解压后的文件：压缩图片、转换音频格式
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class FileProcessingService {

    // 图片和音频文件扩展名
    private static final String[] IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"};
    private static final String[] AUDIO_EXTENSIONS = {".mp3", ".wav", ".flac", ".aac", ".ogg", ".m4a"};
    
    // 临时压缩目录名称
    private static final String COMPRESSED_TEMP_DIR = "compressed_temp";
    
    // 线程池配置
    private static final int PROCESSING_THREADS = 4;
    private static final ExecutorService processingExecutor = Executors.newFixedThreadPool(PROCESSING_THREADS);

    /**
     * 处理解压后的文件：压缩图片、转换音频格式
     * 
     * @param tempDir 临时目录路径
     * @param extractedFiles 解压后的文件列表
     * @return 是否处理成功
     */
    public boolean processExtractedFiles(String tempDir, List<String> extractedFiles) {
        try {
            log.info("开始处理解压后的文件，总数: {}", extractedFiles.size());
            
            // 检查FFmpeg是否可用
            if (!FFmpegUtil.isFFmpegAvailable()) {
                log.error("FFmpeg不可用，无法进行文件处理");
                return false;
            }
            
            // 分类文件
            List<String> imageFiles = new ArrayList<>();
            List<String> audioFiles = new ArrayList<>();
            List<String> otherFiles = new ArrayList<>();
            
            for (String fileName : extractedFiles) {
                String extension = getFileExtension(fileName).toLowerCase();
                if (isImageFile(extension)) {
                    imageFiles.add(fileName);
                } else if (isAudioFile(extension)) {
                    audioFiles.add(fileName);
                } else {
                    otherFiles.add(fileName);
                }
            }
            
            log.info("文件分类完成 - 图片: {}, 音频: {}, 其他: {}", 
                    imageFiles.size(), audioFiles.size(), otherFiles.size());
            
            // 并行处理图片和音频文件
            List<CompletableFuture<Boolean>> futures = new ArrayList<>();
            
            // 处理图片文件
            if (!imageFiles.isEmpty()) {
                futures.add(CompletableFuture.supplyAsync(() -> {
                    try {
                        return processImageFiles(tempDir, imageFiles);
                    } catch (Exception e) {
                        log.error("处理图片文件失败", e);
                        return false;
                    }
                }, processingExecutor));
            }
            
            // 处理音频文件
            if (!audioFiles.isEmpty()) {
                futures.add(CompletableFuture.supplyAsync(() -> {
                    try {
                        return processAudioFiles(tempDir, audioFiles);
                    } catch (Exception e) {
                        log.error("处理音频文件失败", e);
                        return false;
                    }
                }, processingExecutor));
            }
            
            // 等待所有处理完成
            if (!futures.isEmpty()) {
                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(10, TimeUnit.MINUTES);
                
                // 检查所有处理是否成功
                boolean allSuccess = futures.stream()
                    .map(CompletableFuture::join)
                    .allMatch(success -> success);
                
                if (allSuccess) {
                    // 替换原文件
                    replaceOriginalFiles(tempDir);
                    log.info("文件处理完成");
                    return true;
                } else {
                    log.error("部分文件处理失败");
                    return false;
                }
            } else {
                log.info("没有需要处理的文件");
                return true;
            }
            
        } catch (Exception e) {
            log.error("处理解压后文件时发生异常", e);
            return false;
        }
    }

    /**
     * 处理图片文件：生成缩略图
     * 
     * @param tempDir 临时目录
     * @param imageFiles 图片文件列表
     * @return 是否成功
     */
    private boolean processImageFiles(String tempDir, List<String> imageFiles) {
        log.info("开始处理图片文件，数量: {}", imageFiles.size());
        
        int successCount = 0;
        for (String fileName : imageFiles) {
            try {
                String filePath = tempDir + File.separator + fileName;
                File imageFile = new File(filePath);
                
                if (!imageFile.exists()) {
                    log.warn("图片文件不存在: {}", filePath);
                    continue;
                }
                
                // 创建临时压缩目录
                String compressedDir = createCompressedTempDir(tempDir, fileName);
                
                // 压缩图片
                String compressedPath = compressedDir + File.separator + getFileNameFromPath(fileName);
                if (FFmpegUtil.compressImage(filePath, compressedPath)) {
                    successCount++;
                    log.info("图片压缩完成: {} -> {}", fileName, compressedPath);
                } else {
                    log.error("图片压缩失败: {}", fileName);
                }
                
            } catch (Exception e) {
                log.error("处理图片文件失败: {}", fileName, e);
            }
        }
        
        log.info("图片文件处理完成，成功: {}/{}", successCount, imageFiles.size());
        return successCount == imageFiles.size();
    }

    /**
     * 处理音频文件：转换为AAC格式
     * 
     * @param tempDir 临时目录
     * @param audioFiles 音频文件列表
     * @return 是否成功
     */
    private boolean processAudioFiles(String tempDir, List<String> audioFiles) {
        log.info("开始处理音频文件，数量: {}", audioFiles.size());
        
        int successCount = 0;
        for (String fileName : audioFiles) {
            try {
                String filePath = tempDir + File.separator + fileName;
                File audioFile = new File(filePath);
                
                if (!audioFile.exists()) {
                    log.warn("音频文件不存在: {}", filePath);
                    continue;
                }
                
                // 创建临时压缩目录
                String compressedDir = createCompressedTempDir(tempDir, fileName);
                
                // 转换音频格式
                String convertedPath = compressedDir + File.separator + 
                    getFileNameWithoutExtension(fileName) + ".aac";
                if (FFmpegUtil.convertAudioToAAC(filePath, convertedPath)) {
                    successCount++;
                    log.info("音频转换完成: {} -> {}", fileName, convertedPath);
                } else {
                    log.error("音频转换失败: {}", fileName);
                }
                
            } catch (Exception e) {
                log.error("处理音频文件失败: {}", fileName, e);
            }
        }
        
        log.info("音频文件处理完成，成功: {}/{}", successCount, audioFiles.size());
        return successCount == audioFiles.size();
    }

    /**
     * 创建临时压缩目录
     * 
     * @param tempDir 临时目录
     * @param fileName 文件名
     * @return 临时压缩目录路径
     */
    private String createCompressedTempDir(String tempDir, String fileName) throws Exception {
        // 获取源文件所在的目录
        File sourceFile = new File(tempDir, fileName);
        String parentDir = sourceFile.getParent();
        
        // 在父目录下创建临时压缩目录
        String compressedDir = parentDir + File.separator + COMPRESSED_TEMP_DIR;
        File compressedDirFile = new File(compressedDir);
        
        if (!compressedDirFile.exists()) {
            compressedDirFile.mkdirs();
        }
        
        return compressedDir;
    }

    /**
     * 替换原文件：将压缩后的文件替换原文件
     * 
     * @param tempDir 临时目录
     */
    private void replaceOriginalFiles(String tempDir) throws Exception {
        log.info("开始替换原文件");
        
        // 遍历所有目录，查找临时压缩文件夹
        File tempDirFile = new File(tempDir);
        replaceFilesInDirectory(tempDirFile);
        
        log.info("文件替换完成");
    }

    /**
     * 递归替换目录中的文件
     * 
     * @param directory 目录
     */
    private void replaceFilesInDirectory(File directory) throws Exception {
        if (!directory.isDirectory()) {
            return;
        }
        
        File[] files = directory.listFiles();
        if (files == null) {
            return;
        }
        
        for (File file : files) {
            if (file.isDirectory()) {
                if (COMPRESSED_TEMP_DIR.equals(file.getName())) {
                    // 找到临时压缩目录，替换原文件
                    replaceFilesFromCompressedDir(file, directory);
                } else {
                    // 递归处理子目录
                    replaceFilesInDirectory(file);
                }
            }
        }
    }

    /**
     * 从临时压缩目录替换原文件
     * 
     * @param compressedDir 临时压缩目录
     * @param originalDir 原文件目录
     */
    private void replaceFilesFromCompressedDir(File compressedDir, File originalDir) throws Exception {
        File[] compressedFiles = compressedDir.listFiles();
        if (compressedFiles == null) {
            return;
        }
        for (File compressedFile : compressedFiles) {
            if (compressedFile.isFile()) {
                String fileName = compressedFile.getName();
                String extension = getFileExtension(fileName);
                File originalFile;
                if (isImageFile(extension)) {
                    // 图片：同名同后缀
                    originalFile = new File(originalDir, fileName);
                } else if (".aac".equalsIgnoreCase(extension)) {
                    // 音频：aac，找同名MP3（不区分大小写）
                    String baseName = getFileNameWithoutExtension(fileName);
                    File mp3FileUpper = new File(originalDir, baseName + ".MP3");
                    File mp3FileLower = new File(originalDir, baseName + ".mp3");
                    if (mp3FileUpper.exists()) {
                        originalFile = mp3FileUpper;
                    } else if (mp3FileLower.exists()) {
                        originalFile = mp3FileLower;
                    } else {
                        // 没有找到同名MP3，按原逻辑用aac名
                        originalFile = new File(originalDir, fileName);
                    }
                } else {
                    // 其他类型，按原逻辑
                    originalFile = new File(originalDir, fileName);
                }
                // 删除原文件
                if (originalFile.exists()) {
                    originalFile.delete();
                    log.info("删除原文件: {}", originalFile.getAbsolutePath());
                }
                // 移动压缩后的文件到原位置
                File targetFile = new File(originalDir, fileName);
                Files.move(compressedFile.toPath(), targetFile.toPath(), 
                    StandardCopyOption.REPLACE_EXISTING);
                log.info("替换文件完成: {} -> {}", 
                    compressedFile.getAbsolutePath(), targetFile.getAbsolutePath());
            }
        }
        // 删除临时压缩目录
        compressedDir.delete();
        log.info("删除临时压缩目录: {}", compressedDir.getAbsolutePath());
    }

    /**
     * 判断是否为图片文件
     * 
     * @param extension 文件扩展名
     * @return 是否为图片文件
     */
    private boolean isImageFile(String extension) {
        for (String imgExt : IMAGE_EXTENSIONS) {
            if (imgExt.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否为音频文件
     * 
     * @param extension 文件扩展名
     * @return 是否为音频文件
     */
    private boolean isAudioFile(String extension) {
        for (String audioExt : AUDIO_EXTENSIONS) {
            if (audioExt.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件扩展名
     * 
     * @param fileName 文件名
     * @return 扩展名（包含点）
     */
    private String getFileExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "";
        }
        
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            return fileName.substring(lastDotIndex);
        }
        return "";
    }

    /**
     * 获取不带扩展名的文件名
     * 
     * @param fileName 文件名
     * @return 不带扩展名的文件名
     */
    private String getFileNameWithoutExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "";
        }
        
        int lastDotIndex = fileName.lastIndexOf('.');
        return lastDotIndex > 0 ? fileName.substring(0, lastDotIndex) : fileName;
    }

    /**
     * 从路径中获取文件名
     * 
     * @param path 文件路径
     * @return 文件名
     */
    private String getFileNameFromPath(String path) {
        if (path == null || path.isEmpty()) {
            return "";
        }
        
        int lastSeparatorIndex = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
        return lastSeparatorIndex >= 0 && lastSeparatorIndex < path.length() - 1 
            ? path.substring(lastSeparatorIndex + 1) : path;
    }
} 